﻿@page "/"
@using MediaSession.Blazor.Components
@using MediaSession.Blazor.Components.Types

@inject IHowl Howl
@inject IMediaSession MediaSession

<PageTitle>MediaSession.Blazor</PageTitle>

<Card Title="Control">
    @if(_currentItem != null)
    {
        <Card Title="Now Playing" Style="width: 400px;" Bordered>
            <CardMeta AvatarTemplate="@avatarTemplate(_currentItem.Cover)"
                Title="@_currentItem.Title"
                Description="@($"{_currentItem.Artist} - {_currentItem.Album}")" />
        </Card>
        <br />
    }
    <Slider TValue="double" Value="@_sliderValue" Max="@_sliderMax" Step="1" OnChange="OnSliderValueChange" OnAfterChange="OnSliderValueChange" TipFormatter="TipFormatter" />
    <br />
    <Space>
        <SpaceItem>
            <Button @onclick="MovePrevious" Icon="@IconType.Fill.StepBackward">Previous</Button>
        </SpaceItem>
        <SpaceItem>
            @if(_playbackState == PlaybackState.Playing)
            {
                <Button @onclick="Pause" Icon="@IconType.Fill.PauseCircle">Pause</Button>
            }else
            {
                <Button @onclick="Play" Icon="@IconType.Fill.PlayCircle">Play</Button>
            }
        </SpaceItem>
        <SpaceItem>
            <Button @onclick="MoveNext" Icon="@IconType.Fill.StepForward">Next</Button>
        </SpaceItem>
    </Space>
    <br />
    <br />
    <Space>
        <SpaceItem>
            @if(isPictureInPictureEnabled)
            {
                <Button @onclick="() => MediaSession.ExitPictureInPicture()" Icon="@IconType.Outline.VerticalAlignBottom">Exit Picture-in-Picture Mode</Button>
            }
            else
            {
                <Button @onclick="() => MediaSession.RequestPictureInPicture()" Icon="@IconType.Outline.VerticalAlignTop">Enter Picture-in-Picture Mode</Button>
            }
        </SpaceItem>
    </Space>
</Card>
<br />
<Card Title="Playlist">
    <Table TItem="MusicItem" DataSource="@MusicItems" Size="TableSize.Small">
        <Column @bind-Field="@context.Title"></Column>
        <Column @bind-Field="@context.Artist"></Column>
        <Column @bind-Field="@context.Album"></Column>
        <Column @bind-Field="@context.Cover">
            <Image Width="80" Height="80" Src="@context.Cover"/>
        </Column>
        <ActionColumn Title="Action">
            <Space Size="@("middle")">
                <SpaceItem>
                    @if(context != _currentItem)
                    {
                        <Button @onclick="() => Play(context)">Play</Button>
                    }
                    else
                    {
                        <Tag Color="green">Now Playing</Tag>
                    }
                </SpaceItem>
            </Space>
        </ActionColumn>
    </Table>
</Card>

@code{
    private MusicItem _currentItem;
    private int? _soundId;
    private PlaybackState _playbackState = PlaybackState.None;
    private RenderFragment avatarTemplate(string url) => @<Avatar Src="@url" />
    ;
    private System.Timers.Timer _timer;
    private double _sliderValue = 0;
    private double _sliderMax = 0;
    private bool isPictureInPictureEnabled = false;

    protected override async Task OnInitializedAsync()
    {
        await MediaSession.SetDebugMode(true);
        isPictureInPictureEnabled = await MediaSession.IsPictureInPictureEnabled();
        _timer = new(1000);
        _timer.Elapsed += async (s, e) =>
        {
            if (!_soundId.HasValue || _playbackState != PlaybackState.Playing) return;
            var current = await Howl.GetCurrentTime(_soundId.Value);
            var total = await Howl.GetTotalTime(_soundId.Value);
        await MediaSession.SetPositionState(new()
        {
            Duration = total.TotalSeconds,
            Position = current.TotalSeconds
        });
        await InvokeAsync(() =>
            {
                _sliderValue = (int)current.TotalSeconds;
                _sliderMax = (int)total.TotalSeconds;
                StateHasChanged();
            });
        };
        _timer.Enabled = true;
        MediaSession.OnMediaSessionAction += async (s, e) =>
        {
            Console.WriteLine("OnMediaSessionAction: " + e.Action.ToString());
            switch (e.Action)
            {
                case MediaSessionActionTypes.Pause:
                    if (_soundId.HasValue)
                        await Howl.Pause(_soundId.Value);
                    break;
                case MediaSessionActionTypes.Play:
                    if (_soundId.HasValue)
                        await Howl.Play(_soundId.Value);
                    break;
                case MediaSessionActionTypes.NextTrack:
                    await MoveNext();
                    break;
                case MediaSessionActionTypes.PreviousTrack:
                    await MovePrevious();
                    break;
                case MediaSessionActionTypes.SeekTo:
                    if (_soundId.HasValue)
                        await Howl.Seek(_soundId.Value, TimeSpan.FromSeconds(e.SeekTime.Value));
                    break;
                default: break;
            }
        };
        MediaSession.OnPictureInPictureEnter += async (s) =>
        {
            await InvokeAsync(() =>
            {
                isPictureInPictureEnabled = true;
                StateHasChanged();
            });
        };
        MediaSession.OnPictureInPictureLeave += async (s) =>
        {
            await InvokeAsync(() =>
            {
                isPictureInPictureEnabled = false;
                StateHasChanged();
            });
        };
        Howl.OnPause += async (e) =>
        {

            await MediaSession.SetPlaybackState(PlaybackState.Paused);
            await InvokeAsync(() =>
            {
                _playbackState = PlaybackState.Paused;
                StateHasChanged();
            });
        };
        Howl.OnPlay += async (e) =>
        {
            await MediaSession.SetPlaybackState(PlaybackState.Playing);
            if(_currentItem != null)
                await MediaSession.SetMediaMetadata(_currentItem.ToMediaMetadata());
            await InvokeAsync(() =>
            {
                _playbackState = PlaybackState.Playing;
                StateHasChanged();
            });
        };
        Howl.OnStop += async (e) =>
        {
            await MediaSession.SetPlaybackState(PlaybackState.None);
            await InvokeAsync(() =>
            {
                _playbackState = PlaybackState.None;
                StateHasChanged();
            });
        };
        Howl.OnEnd += async (e) =>
        {
            await MoveNext();
        };
    }

    private async Task MoveNext()
    {
        int index = _currentItem != null ? MusicItems.IndexOf(_currentItem) : -1;
        index = (index + 1) % MusicItems.Count;
        await Play(MusicItems[index]);
    }

    private async Task MovePrevious()
    {
        int index = _currentItem != null ? MusicItems.IndexOf(_currentItem) : MusicItems.Count;
        index = (index - 1 + MusicItems.Count) % MusicItems.Count;
        await Play(MusicItems[index]);
    }

    private async Task Play(MusicItem item)
    {
        if (_soundId.HasValue)
            await Howl.Stop(_soundId.Value);
        _soundId = await Howl.Play(new HowlOptions
        {
            Sources = new string[] { item.Source },
            Html5 = true
        });
        await InvokeAsync(() =>
        {
            _currentItem = item;
            StateHasChanged();
        });
    }

    private async Task Play()
    {
        if (_soundId == null)
            await MoveNext();
        else
            await Howl.Play(_soundId.Value);
    }

    private async Task Pause()
    {
        if (_soundId.HasValue)
            await Howl.Pause(_soundId.Value);
    }

    private void OnSliderValueChange(double value)
    {
        if(_soundId.HasValue)
            Howl.Seek(_soundId.Value, TimeSpan.FromSeconds(value));
    }

    private string TipFormatter(double value)
    {
        var ts = TimeSpan.FromSeconds(value);
        return ts.ToString(@"mm\:ss");
    }

    private List<MusicItem> MusicItems = new MusicItem[]
    {
        new()
        {
            Title = "嘘の火花",
            Artist = "96猫",
            Album = "嘘の火花",
            Source = "assets/1.mp3",
            Cover = "assets/1.jpg"
        },
        new()
        {
            Title = "太陽と向日葵",
            Artist = "Minto薄荷糖/顾时笙",
            Album = "太阳与向日葵",
            Source = "assets/2.mp3",
            Cover = "assets/2.jpg"
        },
        new()
        {
            Title = "霜雪千年",
            Artist = "洛天依/乐正绫",
            Album = "Vsinger作品集-2",
            Source = "assets/3.mp3",
            Cover = "assets/3.jpg"
        },
    }.ToList();
}